package core;

import com.mysql.jdbc.ResultSet;
import dbManagement.dbManager;
import dbManagement.notMyPatientException;
import hirondelle.date4j.DateTime;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.LinkedList;
import logManagement.Log4k;
import mailManagement.Mailer;
import objects.userKind;
import objects.visitKind;
import objects.Macro;

/**
 *
 * @author administrator
 */
public class VisitListBean implements Serializable{
    private LinkedList<VisitBean> visitList = new LinkedList<VisitBean>();

    /* il medico che gestisce la lista delle visite (parametro formale per le funzioni) */
    private int callingDoctorId; 
    
    /* intervallo di tempo in secondi che serve alla usl per schedulare le prossime visite da prenotare */
    private int interval = 0;
    
    private int selected = -1;
    
    /* flag che indica all'USL se si vuole fare display di tutti i richiami
     * o solo quelli relativi ai pazienti negligenti
     */
    private boolean allReminders = true;

    private String error; /* for Log4k.debug function */   

    private String getNameFromDB(int id, userKind type){
        String res = "";
        dbManager db = new dbManager();
        res = db.getStrNameByID(id, type);
        db.releaseConnection();
        return res;
    }
    
     /**
     * 
     * @param r 
     * Dato r (ottenuto da una query) estrae i dati di ogni visita, 
     * li salva in un oggetto di tipo VisitBean e inserisce l'oggetto in una lista.
     * Questo metodo viene usato solo internamente alla classe, saranno poi altri 
     * metodi a chiamare la query corretta.
     */
    private void parseQuery(ResultSet r){
        error = "Chiamata VisitListBean.parseQuery";
        Log4k.debug(this.getClass().getName(), error);
        int i=0;
        
        this.visitList.clear();
        
        try {
            if(r.first()){
                while (!r.isAfterLast()) {
                    VisitBean v = new VisitBean();
                    
                    v.setVisit_id(r.getInt("record_id"));
                    
                    int patient_id = r.getInt("patient_id");
                    v.setPatient_id(patient_id);
                    String patientName = getNameFromDB(patient_id, userKind.PATIENT);
                    v.setPatient_name(patientName);
                    
                    int doctor_id = r.getInt("doctor_id");
                    v.setDoctor_id(doctor_id);
                    String doctorName = getNameFromDB(doctor_id, userKind.DOCTOR);
                    v.setDoctor_name(doctorName);
                    
                    v.setProgram_id(r.getInt("program_id"));
                    v.setDate(new DateTime(r.getString("date")));
                
                    String t = r.getString("kind");
                    t.trim();
                    t.toLowerCase();
                    if(t.equals("vaccination"))v.setType(visitKind.VACCINATION);
                    else if (t.equals("programmed")) v.setType(visitKind.PROGRAMMED);
                    else if (t.equals("patient_request")) v.setType(visitKind.PATIENT_REQUEST);
                    else {
                    error = "Nessuno dei tipi dell'enum Ã¨ risultato valido! E' stato ottenuto il valore "+t;
                    Log4k.warn(this.getClass().getName(), error);
                    }
                    //error = "Il valore inserito e': "+t+" e ho ottenuto il tipo "+v.getType();
                    //Log4k.debug(this.getClass().getName(), error);        
                            
                    v.setReport(r.getString("report"));
                    v.setReminder(r.getInt("reminder"));
                    v.setVisited(r.getBoolean("visit_done"));
                    
                    /* Caricamento nomi per rendere la stampa piu' userfriendly */
                    dbManager db = new dbManager();
                    v.setPatient_name(db.getStrNameByID(v.getPatient_id(), userKind.PATIENT));
                    v.setDoctor_name(db.getStrNameByID(v.getDoctor_id(), userKind.DOCTOR));
                    db.releaseConnection();
                    
                    error="Recuperata visita numero = "+(i+1);
                    Log4k.debug(this.getClass().getName(), error);
                    
                    v.setIndex(i);
                    
                    visitList.add(v);
                    r.next();
                    
                    i++;
                    
                    error="aggiunto utente numero = "+i;
                    Log4k.debug(this.getClass().getName(), error);
                }
                error = "Fine del Parsing. Record recuperati: "+i;
                Log4k.debug(this.getClass().getName(), error);
            }
            else{
                error="Recuperata una lista vuota, potrebbe non esserci nessuna visita o essersi verificato un errore!";
                Log4k.warn(this.getClass().getName(), error);
            }
        } catch (SQLException ex) {
            String msg = "Si e' verificato un errore durante la creazione della lista";
            Log4k.error(this.getClass().getName(), msg + ": " + ex.getMessage());
        }
    }
        
    /* da chiamare solo dopo aver settato callingDoctorId */
    public void myScheduledVisits (){
        error = "Chiamata myScheduledVisits";
        Log4k.debug(this.getClass().getName(), error);
        dbManager db = new dbManager();
        parseQuery(db.getMyScheduledVisits(callingDoctorId, userKind.DOCTOR));
        db.releaseConnection();
    }
    
    public LinkedList futureVisits(int patID, userKind ukind){
        error = "Chiamata futureVisits(int, userKind)";
        Log4k.debug(this.getClass().getName(), error);
        dbManager db = new dbManager();
        parseQuery(db.getNextVisits(patID, ukind));
        db.releaseConnection();
        return this.visitList;
    }
    
    public LinkedList firstVisits(int patID, userKind ukind, int limit){
        error = "Chiamata firstVisits(int, userKind)";
        Log4k.debug(this.getClass().getName(), error);
        dbManager db = new dbManager();
        parseQuery(db.getNextNVisits(patID, ukind, limit));
        db.releaseConnection();
        return this.visitList;
    }
    
    public LinkedList futureVisits(int docId, int patID, userKind ukind){
        error = "Chiamata futureVisits(int, int, userKind)";
        Log4k.debug(this.getClass().getName(), error);
        dbManager db = new dbManager();
        try {
            parseQuery(db.getNextVisits(docId, patID, ukind));
        } catch (notMyPatientException ex) {
            error = "Error in futureVisit: ";
            Log4k.warn(this.getClass().getName(), error+ex.getMessage());
        }
        db.releaseConnection();
        return this.visitList;
    }
    
    public void examineFromList(){
        
        
        if (getSelected() >= 0){
            this.visitList.get(getSelected()).flushMessages();
            
            if (this.visitList.get(getSelected()).getReport() != null && !this.visitList.get(getSelected()).getReport().equals("")){
                this.visitList.get(getSelected()).examine();
                this.visitList.remove(this.getSelected());
                setSelected(-1);
            }
            else{
                this.visitList.get(getSelected()).setVoidReport(true);
            }
        }
        else{
            error = "Si cerca di esaminare una visita senza aver settato l'id";
            Log4k.warn(this.getClass().getName(), error);
        }
    }
    
    
    public LinkedList pastVisits(int patID, userKind ukind){
        error = "Chiamata pastVisits";
        Log4k.debug(this.getClass().getName(), error);

        dbManager db = new dbManager();
        parseQuery(db.getPatientHistory(patID, ukind));
        db.releaseConnection();

        error = "Fine pastVisit. Elementi nella lista: "+this.visitList.size();
        Log4k.debug(this.getClass().getName(), error);
        return this.visitList;
    }
    
    public LinkedList pastVisits(int docId, int patID, userKind ukind){
        error = "Chiamata pastVisits";
        Log4k.debug(this.getClass().getName(), error);
        try {
            dbManager db = new dbManager();
            parseQuery(db.getPatientHistory(docId, patID, ukind));
            db.releaseConnection();
        } catch (notMyPatientException ex) {
            Log4k.warn(this.getClass().getName(), ex.getMessage());
        }
        error = "Fine pastVisit. Elementi nella lista: "+this.visitList.size();
        Log4k.debug(this.getClass().getName(), error);
        return this.visitList;
    }

    private void skippedVisits(){
        error = "Chiamata reminderVisits";
        Log4k.debug(this.getClass().getName(), error);
        dbManager db = new dbManager();
        ResultSet r = db.getSkippedVisits(userKind.USL);
        error = "getSkippedVisits ha restituito resultset: "+r;
        Log4k.debug(this.getClass().getName(), error);
        parseQuery(r);
        db.releaseConnection();
    }
    
    private void allVisitsToRemind(){
        error = "Chiamata a toRemindVisits";
        Log4k.debug(this.getClass().getName(), error);
        dbManager db = new dbManager();
        ResultSet r = db.retrieveVisitsToRemind(interval, userKind.USL);
        error = "retrieveVisitsToRemind ha restituito resultset: "+r;
        Log4k.debug(this.getClass().getName(), error);
        parseQuery(r);
        db.releaseConnection();
    }
    
    
    public void toRemindVisits(){
        if (this.allReminders){
            this.allVisitsToRemind();
        } else {
            this.skippedVisits();
        }
    }
    
    /* A questo punto la visitList deve essere composta da tutte quelle visite
     * che devono essere rammentate ai pazienti (i.e. toRemindVisits deve
     * gia' essere stata chiamata)
     * callingDoctorId deve essere correttamente settato
     */
    public void remindVisits(int operatorID){
        error = "Chiamata a remindVisits";
        Log4k.debug(this.getClass().getName(), error);
        
        dbManager db = new dbManager();
        
        /* Invia i richiami a ciascun paziente della visitList e nel frattempo
         * salva in chiefTainList[chiefTain_id] i pazienti richiamati che
         * appartengono al capovillaggio di id chiefTain_id, per ogni capovillaggio.
         * chiefTainList e' un array di liste. Ogni lista corrisponde ad una riga
         * del file txt che si andra'  a comporre dopo. L'array e' grande come tutti
         * i pazienti, in modo da poter accedere alla cella del capovillaggio
         * cosi' come indicato sopra. Molte celle rimangono null e non verranno
         * considerate.
         */        
        String sender = db.getEmailByID(operatorID, userKind.USL);        
        String subject = "Richiamo / Reminder";
        String object = "";
        int chiefTainN = db.getNumberOfPatients();
        LinkedList[] chiefTainList = new LinkedList[chiefTainN];
        for (VisitBean v : visitList){
            if (!db.isNotified(v.getVisit_id())){ // per risolvere problemi di possibile concorrenza
                String[] receiver = {db.getEmailByID(v.getPatient_id(), userKind.PATIENT)};
                String stressedReminders = "\trichiami gia' effettuati / sent reminders = "
                        + v.getReminder();
                object = "Le inviamo questa mail per ricordarLe che in data "
                        + v.getDate() + " e' programmata una visita presso il dr. " + v.getDoctor_name();
                object += "\n\n----------------------------------------"
                        + "----------------------------------------\n\n";
                object += "This mail has been sent to remind you that on " + v.getDate()
                        + " you are scheduled to attend a medical examination "
                        + "with dr. " + v.getDoctor_name();
                if (v.getReminder() >= 2)
                    object += "\n" + stressedReminders;
                Mailer.mail(sender, receiver, subject, object);
                db.sumAReminder(v.getVisit_id());
                
                int chiefTain_id = db.getChiefTainByID(v.getPatient_id());
                if (chiefTainList[chiefTain_id] == null)
                    chiefTainList[chiefTain_id] = new LinkedList();
                String chiefTainListLine = v.getPatient_id() + " " + v.getPatient_name();
                if (v.getReminder() >= 2)
                    chiefTainListLine += stressedReminders;
                chiefTainList[chiefTain_id].add(chiefTainListLine);
            }
        }     
        
        /* Scrivi una mail ai capovillaggi indicando quali pazienti sono stati
         * richiamati, sfruttando la chiefTainList */
        subject = "Lista dei richiami / Reminders list";
        object = "Lei riceve questa mail perche' le persone nel file allegato sono state"
                + "richiamate per una visita ed appartengono al Suo villaggio.";
        object += "\n\n----------------------------------------"
                    + "----------------------------------------\n\n";
        object += "You are receiving this mail after the people in the attachment have "
                + "been reminded of a visit and they belong to your village.";
                object += "\n\n";
        for (int i=0; i<chiefTainList.length; i++){
            if (chiefTainList[i] != null){
                BufferedWriter out = null;
                try {
                    String attachName = Macro.localPath("reminders_list.txt");
                    out = new BufferedWriter(new FileWriter(attachName));
                    while (!chiefTainList[i].isEmpty()){
                        out.write((String)chiefTainList[i].remove() + "\n");
                    }
                    out.close();
                    String[] chiefTain_addr = {db.getEmailByID(i, userKind.PATIENT)};
                    Mailer.mail(Macro.SYSTEM_ADDRESS, chiefTain_addr, subject, object, attachName);
                } catch (IOException ex) {
                    Log4k.error(this.getClass().getName(), ex.getMessage());
                } finally {
                    try {
                        out.close();
                    } catch (Exception ex) {
                        Log4k.error(this.getClass().getName(), ex.getMessage());
                    }
                }
            }
        }        
        db.releaseConnection();
    }
    
    /**
     * @return the visitList
     */
    public LinkedList<VisitBean> getVisitList() {
        return visitList;
    }

    /**
     * @param visitList the visitList to set
     */
    public void setVisitList(LinkedList<VisitBean> visitList) {
        this.visitList = visitList;
    }

    /**
     * @return the callingDoctorId
     */
    public int getCallingDoctorId() {
        return callingDoctorId;
    }

    /**
     * @param callingDoctorId the callingDoctorId to set
     */
    public void setCallingDoctorId(int callingDoctorId) {
        this.callingDoctorId = callingDoctorId;
    }

        /**
     * @return the interval
     */
    public int getInterval() {
        return interval;
    }

    /**
     * @param interval the interval to set
     */
    public void setInterval(int interval) {
        this.interval = interval;
    }

    public boolean isAllReminders() {
        return allReminders;
    }

    public void setAllReminders(boolean allReminders) {
        this.allReminders = allReminders;
    }

    /**
     * @return the selected
     */
    public int getSelected() {
        return selected;
    }

    /**
     * @param selected the selected to set
     */
    public void setSelected(int selected) {
        this.selected = selected;
    }
    
}
